package demo.mq.rabbit;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * <h1>Exchanges概念</h1>RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上，通常生产 者甚至都不知道这些消息传递传递到了哪些队列中。
 * 相反，生产者只能将消息发送到交换机(exchange)，交换机工作的内容非常简单，一方面它接收来自生产者的消息，另一方面将它们推入队列。
 * 交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们到许多队列中还是说应该丢弃它们。这就的由交换机的类型来决定。
 * <p>
 * Exchanges总共有以下类型
 *
 * <li>直接（direct）
 * <li>主题（topic）
 * <li>标题（header）
 * <li>扇出（fanout）
 */
public class ExchangeDemo {
    String queueName;
    String message;
    Channel channel;
    String exchangeName = "logs";

    /**
     * 无名类型的exchange为默认类型，通常使用空字符串进行标识
     */
    public void defaultExchange() throws IOException {
        //其中第一个参数就是exchange交换机的无名类型，也就是默认类型
        channel.basicPublish("", queueName, null, message.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 临时队列：每当我们连接到 Rabbit 时，我们都需要一个全新的空队列，为此我们可以创建一个具有随机名称 的队列，或者能让服务器为我们选择一个随机队列名称那就更好了。
     * 其次一旦我们断开了消费者的连接，队列将被自动删除。 临时队列和非临时队列主要看队列是否持久化。创建临时队列的方式是我们在创建信道的时候不给队列设置名称即可
     */
    public void tempQueue() throws IOException {
        String queueName = channel.queueDeclare().getQueue();

    }

    /**
     * 绑定：binding 其实是 exchange 和 queue 之间的桥梁，它告诉我们 exchange 和那个队 列进行了绑定关系。
     */
    public void bindings() {

    }

    /**
     * Fanout 这种类型非常简单。正如从名称中猜到的那样，它是将接收到的所有消息广播到它知道的 所有队列中。
     * <a href= "https://www.rabbitmq.com/tutorials/tutorial-three-java.html">参考<a/>
     * 消息持久化：
     * <li>1、 设置队列 durable = true
     * <li>2、发送的消息指定属性MessageProperties.PERSISTENT_TEXT_PLAIN
     */
    public void fanout() throws IOException {
        // 声明一个交换机
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT);
        //声明一个临时队列
        String queueName = channel.queueDeclare().getQueue();
        //绑定交换机
        channel.queueBind(queueName, exchangeName, "");
        System.out.println("C1等待接收消息");
        //接收消息

        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("C1成功接收到消息: " + new String(message.getBody()));
            // 消息手动应答
            channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
        };

        CancelCallback cancelCallback = consumerTag -> {
            System.out.println("C1接收失败的消息的队列标记值: " + consumerTag);
        };

        //消费者取消消息时触发
        /*
         *
         * 参数一: 队列名称
         * 参数二: 是否自动应答
         * 参数三: 成功确认消息回调函数
         * 参数四: 失败消息回调函数
         * */
        boolean autoAck = false;
        channel.basicConsume(queueName, autoAck, deliverCallback, cancelCallback);
    }
}
